package com.ecas.core.shiro.filter;


import com.alibaba.fastjson.JSONObject;
import com.ecas.core.common.redis.RedisUtil;
import com.ecas.core.shiro.session.EcasSessiondDao;

import com.ecas.core.util.PropertiesFileUtil;
import com.ecas.core.util.RequestParameterUtil;
import com.google.common.base.Strings;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;


/**
 * 重写authc过滤器
 * Created by devon on 2017/12/21.
 */
public class EcasAuthenticationFilter extends AuthenticationFilter {

    private static final Logger LOGGER = LoggerFactory.getLogger(EcasAuthenticationFilter.class);

    // 局部会话key
    private static final String CLIENT_SESSION_ID = "client-session-id";
    // 单点同一个code所有局部会话key
    private static final String CLIENT_SESSION_IDS = "client-session-ids";

    @Autowired
    EcasSessiondDao ecasSessionDao;

    /**
     * 身份验证
     *
     * @param request
     * @param response
     * @param mappedValue
     * @return
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        LOGGER.debug("isAccessAllowed,request:{}, response:{},mappedValue:{}", request, response, mappedValue);
        Subject subject = getSubject(request, response);
        if (!subject.isAuthenticated() && !subject.isRemembered()) {
            //验证不通过走验证流程
            LOGGER.debug("isAccessAllowed is  refuse! it will onAccessDenied method");
            return false;
        }
        return subject.isAuthenticated();
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        LOGGER.debug("onAccessDenied,request:{}, response:{}", request, response);

        return validateClient(request, response);
    }

    /**
     * 认证中心登录成功带回code
     *
     * @param request
     */
    private boolean validateClient(ServletRequest request, ServletResponse response) {
        LOGGER.debug("validateClient,request:{}, response:{}", request, response);

        Subject subject = getSubject(request, response);
        Session session = subject.getSession();
        String sessionId = session.getId().toString();
        int timeOut = (int) session.getTimeout() / 1000;
        // 判断局部会话是否登录
        String cacheClientSession = RedisUtil.get(CLIENT_SESSION_ID + "_" + session.getId());
        if (StringUtils.isNotBlank(cacheClientSession)) {
            // 更新code有效期
            RedisUtil.set(CLIENT_SESSION_ID + "_" + sessionId, cacheClientSession, timeOut);
            Jedis jedis = RedisUtil.getJedis();
            jedis.expire(CLIENT_SESSION_IDS + "_" + cacheClientSession, timeOut);
            jedis.close();
            // 移除url中的code参数
            if (null != request.getParameter("code")) {
                String backUrl = RequestParameterUtil.getParameterWithOutCode(WebUtils.toHttp(request));
                HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
                try {
                    httpServletResponse.sendRedirect(backUrl.toString());
                } catch (IOException e) {
                    LOGGER.error("局部会话已登录，移除code参数跳转出错：", e);
                }
            } else {
                return true;
            }
        }
        // 判断是否有认证中心code
        String code = request.getParameter("upms_code");
        // 已拿到code
        if (Strings.isNullOrEmpty(code)) {
            return false;
        }

        // HttpPost去校验code
        DefaultHttpClient httpclient = null;
        try {
            StringBuilder ssoServerUrl = new StringBuilder(PropertiesFileUtil.getInstance("zheng-upms-client").get("zheng.upms.sso.server.url"));
            httpclient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(ssoServerUrl.toString() + "/sso/code");

            List<NameValuePair> nameValuePairs = new ArrayList<>();
            nameValuePairs.add(new BasicNameValuePair("code", code));
            httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs));

            HttpResponse httpResponse = httpclient.execute(httpPost);
            if (httpResponse.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
                return false;
            }

            HttpEntity httpEntity = httpResponse.getEntity();
            JSONObject result = JSONObject.parseObject(EntityUtils.toString(httpEntity));
            if (1 == result.getIntValue("code") && result.getString("data").equals(code)) {
                // code校验正确，创建局部会话
                RedisUtil.set(CLIENT_SESSION_ID + "_" + sessionId, code, timeOut);
                // 保存code对应的局部会话sessionId，方便退出操作
                RedisUtil.sadd(CLIENT_SESSION_IDS + "_" + code, sessionId, timeOut);
                LOGGER.debug("当前code={}，对应的注册系统个数：{}个", code, RedisUtil.getJedis().scard(CLIENT_SESSION_IDS + "_" + code));
                // 移除url中的token参数
                String backUrl = RequestParameterUtil.getParameterWithOutCode(WebUtils.toHttp(request));

                // client无密认证
                String username = request.getParameter("upms_username");
                subject.login(new UsernamePasswordToken(username, ""));
                HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
                httpServletResponse.sendRedirect(backUrl.toString());
                return true;
            } else {
                LOGGER.warn(result.getString("data"));
            }
        } catch (IOException e) {
            LOGGER.error("验证token失败：", e);
        } finally {
            if (httpclient != null) {
                httpclient.close();
            }
        }

        return false;
    }

}
